﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using WeifenLuo.WinFormsUI.Docking;
using MacomberMapSystem.Common.Database;
using MacomberMapSystem.Common.Internals;
using MacomberMapSystem.Common.Serialization;
using System.IO;
using MacomberMapSystem.Common.CIM.Direct;
using System.Text.RegularExpressions;
using MacomberMapSystem.Common.CIM;
using MacomberMapSystem.Common.Exports;
using System.Data.Common;
using System.Xml;
using System.Threading;

namespace MacomberMapSystem.Common.User_Interfaces.One_Lines
{
    /// <summary>
    /// This form provides an interface for direct model validation/editing of onelines (e.g., RARFs)
    /// </summary>
    public partial class frmDirectOneLine : DockContent
    {
        #region Variable declarations
        /// <summary>
        /// The current model
        /// </summary>
        public MM_Database_Model Model
        {
            get
            {
                Object OutModel;
                if (!NamedObjects.TryGetValue("Model", out OutModel))
                    NamedObjects.Add("Model", OutModel = new MM_Database_Model());
                return OutModel as MM_Database_Model;
            }
            set
            {
                NamedObjects.Remove("Model");
                NamedObjects.Add("Model", value);
            }
        }

        /// <summary>Our collection of named objects</summary>
        public Dictionary<String, Object> NamedObjects = new Dictionary<string, object>();

        /// <summary>Our repository</summary>
        private MM_Repository Repository;

        /// <summary>Our collection of substations</summary>
        private List<CIM_Element> Substations = new List<CIM_Element>();
        #endregion

        #region Initialization
        /// <summary>
        /// Handle the initialization of our direct model editor
        /// </summary>
        public frmDirectOneLine(MM_Repository Repository)
        {
            InitializeComponent();
            this.Repository = Repository;
        }
        #endregion



        /// <summary>
        /// Handle the loading of our model
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void lblModelName_Paint(object sender, PaintEventArgs e)
        {
            //If our model is loading, update our background
            if (Model.State != MM_Database_Model.enumModelState.Completed && Model.State != MM_Database_Model.enumModelState.Unopened)
            {
                //Blank our background, draw our progress bar, and percentages
                e.Graphics.FillRectangle(SystemBrushes.Control, e.ClipRectangle);
                RectangleF DrawBounds = new RectangleF(0, 0, lblModelName.Width * Model.StepProgress, lblModelName.Height);
                Rectangle LabelBounds = new Rectangle(Point.Empty, lblModelName.Size);
                e.Graphics.FillRectangle(Brushes.LightBlue, DrawBounds);
                int MarqueeTime = 5;
                if (Model.StepProgress > 0)
                {
                    TimeSpan RunTime = DateTime.Now - Model.StartTime;
                    Double Rem = (RunTime.TotalSeconds * (1 - Model.StepProgress)) / Model.StepProgress;
                    String RemStr = "Infinite";
                    if (Rem < TimeSpan.MaxValue.TotalSeconds)
                    {
                        TimeSpan RemainingTime = TimeSpan.FromSeconds(Rem);
                        RemStr = RemainingTime.TotalHours.ToString("00") + ":" + RemainingTime.Minutes.ToString("00") + ":" + RemainingTime.Seconds.ToString("00");
                    }
                    if (DateTime.Now.Second % (MarqueeTime * 2) < MarqueeTime)
                        e.Graphics.DrawString(lblModelName.Text, lblModelName.Font, Brushes.Black, LabelBounds, MM_OneLine_Element.CenterFormat);
                    else
                        e.Graphics.DrawString(Model.State.ToString().Replace('_', ' ') + " " + Model.StepProgress.ToString("0.0%") + " | " + RemStr, lblModelName.Font, Brushes.Black, LabelBounds, MM_OneLine_Element.CenterFormat);
                }
                else if (DateTime.Now.Second % (MarqueeTime * 2) < MarqueeTime)
                    e.Graphics.DrawString(lblModelName.Text, lblModelName.Font, Brushes.Black, LabelBounds, MM_OneLine_Element.CenterFormat);
                else
                    e.Graphics.DrawString(Model.State.ToString().Replace('_', ' ') + " " + Model.StepProgress.ToString("0.0%"), lblModelName.Font, Brushes.Black, LabelBounds, MM_OneLine_Element.CenterFormat);
            }
        }

        /// <summary>
        /// Handle our update timer
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void tmrUpdate_Tick(object sender, EventArgs e)
        {
            lblModelName.Refresh();
        }

        /// <summary>
        /// Handle opening of a new model
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnOpenModel_Click(object sender, EventArgs e)
        {
            using (OpenFileDialog oFd = new OpenFileDialog())
            {
                oFd.Title = "Select a CIM model to open...";
                oFd.Filter = "CIM files|CIM*.xml;CIM*.SQLite;CIM*.mdb;*.CIMIndex|Xml file (*.xml)|*.xml|CIM Xml file (CIM*.xml)|CIM*.xml|CIM SQLite Index (CIM*.SQLite)|CIM*.SQLite|CIM Mdb Index (CIM*.mdb)|CIM*.mdb|CIM Index (old version)|*.CIMIndex";
                if (oFd.ShowDialog() == DialogResult.OK)
                {
                    btnOpenModel.Tag = oFd.FileName;
                    lblModelName.Text = Path.GetFileNameWithoutExtension(oFd.FileName);

                    //If we're an XML, start opening it up
                    if (Path.GetExtension(oFd.FileName).Equals(".xml", StringComparison.CurrentCultureIgnoreCase) || Path.GetExtension(oFd.FileName).Equals(".cimindex", StringComparison.CurrentCultureIgnoreCase))
                        this.Model = new CIM_DirectModel(oFd.FileName, true, Repository);
                    else
                    {
                        FileInfo fI = new FileInfo(Path.Combine(Repository.Db.ModelIndexPath, Path.GetFileNameWithoutExtension(oFd.FileName) + ".sqlite"));
                        if (fI.Exists)
                            fI.Delete();
                        this.Model = new MM_Database_Model();
                        this.Model.FullPath = Path.GetDirectoryName(oFd.FileName);
                        this.Model.ModelLocation = MM_Database_Model.enumModelLocations.InPrimary;
                        this.Model.Name = Path.GetFileNameWithoutExtension(oFd.FileName);
                        this.Model.Db = Repository.Db;
                        this.Model.InitiateLoading();
                        this.Model.Repository = Repository;
                        new MacomberMapSystem.Common.User_Interfaces.CIM_Browser.frmCIMBrowser(this.Model).Show();
                    }
                    this.Model.StateChanged += new MM_Database_Model.StateChangedDelegate(Model_StateChanged);


                }
            }
        }

        /// <summary>
        /// Update our state as needed
        /// </summary>
        /// <param name="Sender"></param>
        /// <param name="CurrentState"></param>
        /// <param name="PriorState"></param>
        private void Model_StateChanged(MM_Database_Model Sender, MM_Database_Model.enumModelState CurrentState, MM_Database_Model.enumModelState PriorState)
        {
            if (CurrentState == MM_Database_Model.enumModelState.Completed)
                if (cmbSubstation.InvokeRequired)
                    cmbSubstation.Invoke(new MM_Database_Model.StateChangedDelegate(Model_StateChanged), Sender, CurrentState, PriorState);
                else
                {
                    cmbSubstation.Items.Clear();
                    foreach (String Sub in Sender.Substations.Keys)
                        cmbSubstation.Items.Add(Sub);
                }
        }

        /// <summary>
        /// When an index is changed, find and validate all one-lines against the substation.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void cmbSubstation_SelectedIndexChanged(object sender, EventArgs e)
        {
            CIM_Element Sub;
            cmbOneLine.Items.Clear();
            if (!Model.Substations.TryGetValue(cmbSubstation.Text, out Sub))
                return;

            //Pull all one-lines for the current substation            
            List<MM_OneLineValidation> Validations = new List<MM_OneLineValidation>();
            //using (DbCommand dCmd = Repository.Db.CreateCommand("SELECT Document, ID, LastExported FROM MM_DATABASE_ONELINE WHERE rdfID=:0", Sub.rdfID.ToString()))
            using (DbCommand dCmd = Repository.Db.CreateCommand("SELECT * FROM MM_DATABASE_ONELINE WHERE rdfID=:0", Sub.rdfID.ToString()))
            using (DbDataReader dRd = dCmd.ExecuteReader())
                while (dRd.Read())
                    try
                    {
                        Validations.Add(new MM_OneLineValidation(dRd, Sub, Repository));
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine("Error validating " + Sub.ToString() + ": " + ex.Message);
                    }
            Validations.Sort();
            cmbOneLine.Items.AddRange(Validations.ToArray());

            //Add a "new" validator
            XmlDocument NewOL = new XmlDocument();
            XmlElement RootElem = NewOL.CreateElement("One_Line");
            NewOL.AppendChild(RootElem);
            RootElem.Attributes.Append(NewOL.CreateAttribute("BaseElement.ElemType")).Value = "Substation";
            RootElem.Attributes.Append(NewOL.CreateAttribute("BaseElement.TEID")).Value = Sub.TEID;
            RootElem.Attributes.Append(NewOL.CreateAttribute("BaseElement.DisplayName")).Value = MM_Type_Converter.TitleCase(Sub["cim:IdentifiedObject.Description"]);
            RootElem.Attributes.Append(NewOL.CreateAttribute("BaseElement.LongName")).Value = MM_Type_Converter.TitleCase(Sub["cim:IdentifiedObject.Description"]);
            RootElem.Attributes.Append(NewOL.CreateAttribute("BaseElement.Name")).Value = MM_Type_Converter.TitleCase(Sub.Name);
            RootElem.Attributes.Append(NewOL.CreateAttribute("ExportDate")).Value = XmlConvert.ToString(DateTime.Now, XmlDateTimeSerializationMode.Unspecified);
            RootElem.Attributes.Append(NewOL.CreateAttribute("LastCIM")).Value = Model.Name;
            cmbOneLine.Items.Add(new MM_OneLineValidation(NewOL, Sub.Elements.Length));
        }



        /// <summary>
        /// Load the selected one-line in.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void cmbOneLine_SelectedIndexChanged(object sender, EventArgs e)
        {
            MM_OneLineValidation olv = cmbOneLine.SelectedItem as MM_OneLineValidation;
            CIM_Element Sub = Model.Substations[cmbSubstation.Text];
            MM_Database_OneLine TempOL = new MM_Database_OneLine();
            TempOL.Document = olv.xDoc;
            TempOL.ID = -1;
            TempOL.Models = new MM_Database_Model[] { };
            TempOL.Names = new string[] { Sub.Name };
            TempOL.OneLineType = MM_Database_OneLine.OneLineTypeEnum.Substation;
            TempOL.rdfID = Sub.rdfID;
            TempOL.TEID = Convert.ToUInt64(Sub.TEID);
            try
            {
                OneLineEditor.LoadOneLine(TempOL, Model, Sub, null, Repository);
            }
            catch (Exception ex)
            {
                Clipboard.SetText(ex.ToString());
                MessageBox.Show(this, "Error loading one-line: " + ex.ToString(), Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Stop);
            }
        }

        /// <summary>
        /// If the user click on the model tab, pull up our CIM browser
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void lblLoadModel_Click(object sender, EventArgs e)
        {
            if (Model == null || Model.State != MM_Database_Model.enumModelState.Completed)
                return;

            //Try and find our owner form
            WeifenLuo.WinFormsUI.Docking.DockPanel TargetPanel = null;
            Form FormToCheck = this.ParentForm;
            do
            {
                foreach (Control ctl in FormToCheck.Controls)
                    if (ctl is WeifenLuo.WinFormsUI.Docking.DockPanel)
                        TargetPanel = ctl as WeifenLuo.WinFormsUI.Docking.DockPanel;
                FormToCheck = FormToCheck.ParentForm;
            } while (TargetPanel == null && FormToCheck != null);


            new MacomberMapSystem.Common.User_Interfaces.CIM_Browser.frmCIMBrowser(Model).Show(TargetPanel, WeifenLuo.WinFormsUI.Docking.DockState.Document);
        }

        /// <summary>
        /// Handle the application of a NOMCR
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnApplyNOMCR_Click(object sender, EventArgs e)
        {
            using (OpenFileDialog oFd = new OpenFileDialog())
            {
                oFd.Title = "Select a NOMCR file...";
                oFd.Filter = "NOMCR XML file (*.xml)|*.xml";
                if (oFd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
                    ThreadPool.QueueUserWorkItem(new WaitCallback(delegate
                {
                    DateTime StartTime = DateTime.Now;
                    XmlDocument xDoc = new XmlDocument();
                    xDoc.Load(oFd.FileName);
                    Model.LoadIncrementalFile(xDoc, false);
                    cmbSubstation.Invoke((MethodInvoker)delegate
                    {
                        cmbSubstation.Items.Clear();
                        cmbSubstation.Items.AddRange(Model.Substations.Keys.ToArray());
                    });
                    MessageBox.Show("Incremental file loaded in " + (DateTime.Now - StartTime).ToString(), Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Information);
                }));
            }
        }

        /// <summary>
        /// If the model link is clicked, open up
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void lblModel_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            if (Model != null)
                new Common.User_Interfaces.CIM_Browser.frmCIMBrowser(Model).Show((this as DockContent).DockPanel); 
        }
    }
}
